using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using RPGX.Graphics;
using RPGX.Scripting.SystemFunctions;
using RPGXEngine;
using System.Globalization;
using RPGX.Network;
using RPGX.Game;
using RPGXEngine.World;
using RPGXEngine.Factories;
using RPGXEngine.Object;

namespace RPGX
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region Static Properties

        public static Game1 Instance
        {
            get { return Game1._instance; }
            set { Game1._instance = value; }
        }
        private static Game1 _instance = null;

        #endregion

        #region Properties

        public GraphicsSystem Graphics
        {
            get { return _graphics; }
            set { _graphics = value; }
        }
        private GraphicsSystem _graphics = null;

        public ScriptManager ScriptManager
        {
            get { return _scriptManager; }
            set { _scriptManager = value; }
        }
        private ScriptManager _scriptManager = new ScriptManager();

        public HUD HUD
        {
            get { return _hud; }
            set { _hud = value; }
        }
        private HUD _hud = new HUD();

        public GameText GameText
        {
            get { return _gameText; }
            set { _gameText = value; }
        }
        private GameText _gameText = null;

        public NetworkManager NetworkManager
        {
            get { return _networkManager; }
            set { _networkManager = value; }
        }
        private NetworkManager _networkManager = null;

        public GameClient GameClient
        {
            get { return _gameClient; }
            set { _gameClient = value; }
        }
        private GameClient _gameClient = new GameClient();

        public GameWorld GameWorld
        {
            get { return _gameWorld; }
            set { _gameWorld = value; }
        }
        private GameWorld _gameWorld = new GameWorld();

        public GameState GameState
        {
            get { return _gameState; }
            set { _gameState = value; }
        }
        private GameState _gameState = GameState.LoginMenu;

        public ILogger Logger
        {
            get { return _logger; }
            set { _logger = value; }
        }
        private ILogger _logger = null;

        #endregion

        #region Public Methods

        public Game1()
        {
            this.Logger = LoggerFactory.Get(); // create new
            this.Logger.LogPath = String.Format("{0}/data/log.txt",
                Environment.CurrentDirectory); // setup
            LoggerFactory.Set(this.Logger); // update for all other instances wanting this static object

            this.NetworkManager = new NetworkManager();

            this.Graphics = new GraphicsSystem(this);

            Content.RootDirectory = "Content";
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            this.Logger.Log("Loading game");

            this.GameText = new GameText(this.Content);
            this.GameText.Load("Data\\Text");

            this.Graphics.LoadContent("Data/Settings/Graphics.set");

            ModelPool.FromXML("Data/Models/models.xml");

            this.LoadScripting();

            this.Logger.Log("Loaded game"); // load the world in the script...
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            this.Logger.Log("UnLoading game");

            this.NetworkManager.UnLoad();

            this.ScriptManager.UnLoad();

            this.Graphics.UnLoad();

            this.GameText.UnLoad();

            this.Logger.Log("UnLoaded game");
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (Keyboard.GetState(PlayerIndex.One).GetPressedKeys().Contains(Keys.Escape))
                this.Exit();

            this.GameWorld.Update(gameTime);

            this.HUD.Update(gameTime);

            this.NetworkManager.Update(gameTime);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            this.Graphics.Render(gameTime, this.HUD, this.GameWorld);

            base.Draw(gameTime);
        }

        public void LoadScripting()
        {
            this.ScriptManager.LoadExtraSystemFunctions += new RPGXEngine.ScriptManager.LoadExtraSystemFunctionsDelegate(ScriptManager_LoadExtraSystemFunctions);

            this.ScriptManager.Load("Data/Scripts");

            this.ScriptManager.Run_OnLoad();
        }

        #endregion

        #region Private Methods

        private void ScriptManager_LoadExtraSystemFunctions(ScriptManager sm)
        {
            #region System Functions

            #region Game Functions

            sm.AddSystemFunction("GameExit",
                new List<IVariableType>()
                {
                },
                SystemFunction_GameExit.Method);

            sm.AddSystemFunction("GameStateSet",
                new List<IVariableType>()
                {
                    new VarVariableType("newState"),
                },
                SystemFunction_GameStateSet.Method);

            #endregion

            #region HUD Functions

            sm.AddSystemFunction("HUDLoad",
                new List<IVariableType>()
                {
                    new VarVariableType("fileName"),
                },
                SystemFunction_HUDLoad.Method);

            sm.AddSystemFunction("HUDTextBoxGetText",
                new List<IVariableType>()
                {
                    new VarVariableType("id"),
                },
                SystemFunction_HUDTextBoxGetText.Method);

            sm.AddSystemFunction("HUDTextAreaSetKey",
                new List<IVariableType>()
                {
                    new VarVariableType("id"),
                    new VarVariableType("key"),
                },
                SystemFunction_HUDTextAreaSetKey.Method);

            #endregion

            #region Network

            sm.AddSystemFunction("NetworkAuthenticate",
                new List<IVariableType>()
                {
                    new VarVariableType("username"),
                    new VarVariableType("password"),
                    new VarVariableType("errorCallBack"),
                    new VarVariableType("successCallBack"),
                },
                SystemFunction_NetworkAuthenticate.Method);

            #endregion

            #region World

            sm.AddSystemFunction("WorldLoad",
                new List<IVariableType>()
                {
                    new VarVariableType("fileName"),
                },
                SystemFunction_WorldLoad.Method);

            #endregion

            #endregion
        }

        #endregion
    }
}
